import threading
import socket
import pickle
from communication import *
import walker
import random
import upload

class Server(threading.Thread):
  def __init__(self, pman, fman, port):
    threading.Thread.__init__(self)
    self._port = port
    self._pman = pman
    self._fman = fman

  def msg(self, cmd, data=None):
    return pickle.dumps((self._pman.name, cmd, data))

  def run(self):
    inp = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    inp.bind(("", self._port))
    inp.listen(5)
    while 1:
      data=""
      conn, addr = inp.accept()
      while 1:
        randominput = conn.recv(1024)
        if not randominput: break
        data += randominput

      name, cmd, data = pickle.loads(data)
      ##If request is a "Hello" message, return plist
      if cmd == HELLO:
        conn.send(self.msg(OK, self._pman.plist))
        ##Retrieve senders info, add to plist if not already present
        self._pman.pappend(data)

      ##Check whether peer is able to accept a new neighbor, add if possible
      elif cmd == NEIGHBOR:
        res = self._pman.nappend(data)
        if res == ACCEPTED:
          res = self.msg(ACCEPTED, self._fman.flist)
          conn.send(res)
        else:
          conn.send(self.msg(REJECTED))

      ##Dropped as neighbor
      elif cmd == DROPPED:
        self._pman.nremove(data)
        conn.send(self.msg(OK))
        if len(self._pman.nlist) < 1:
          self._pman.getneighbors()
        
      ##Return neighborhood list
      elif cmd == NLIST:
        conn.send(self.msg(OK, self._pman.nlist))
        
      ##Return number of neighbors
      elif cmd == NNEIGHBORS:
        conn.send(self.msg(OK, len(self._pman.nlist)))
        
      ##Reply pong to acknowledge peer is alive
      elif cmd == PING:
        if data is not None:
          self._fman.processchangelist(name, data)
          
        conn.send(self.msg(PONG))
        
      ##Update neighbors file list
      elif cmd == NFLIST:
        if self._pman.isneighbor(name):
          self._fman.addnflist(name, data)
        conn.send(self.msg(OK))
        
      ##Requested file has been found
      elif cmd == FOUND:
        conn.send(self.msg(OK))
        fname, peer = data
        self._pman.file_found(fname, peer)

      ##Find request send by neighbor
      elif cmd == FIND:
        conn.send(self.msg(OK))
        fname, ttl, req = data
        peer = self._fman.find(fname)
        
        if peer is not None:
          pname, neighbors, ip, port = req
          fromname, cmd, data = comm(ip, port, self._pman.name, FOUND, (fname, peer))
        else:
          ttl = ttl - 1
          if ttl != 0 and len(self._pman.nlist) > 1:
            prec = random.choice(self._pman.nlist)
            while prec[0] == name:
              prec = random.choice(self._pman.nlist)
            
            walk = walker.Walker(ttl, fname, prec, self._pman.plist[0], req)
            walk.run()
            
      ##Download request by 
      elif cmd == GET:
        conn.send(self.msg(OK))
        fname, peer = data
        pname, neightbor, ip, port = peer
        
        up = upload.Uploader(ip, port, fname, self._pman)
        up.setDaemon(1)
        up.start()
        
      elif cmd == FILE:
        conn.send(self.msg(OK))
        fname, content = data
        self._fman.write_file(fname, content)
        
      conn.shutdown(socket.SHUT_WR)
      conn.close()
       
